package com.dark.util;

import cn.hutool.core.io.FileUtil;
import com.dark.base.FlowerFallingException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

/**
 * ClassName: FileUtils
 * Description:FileUtils
 *
 * @author chengluchao
 * @date: 2022/8/25 9:26 AM
 * @since 1.4.9
 */
@Slf4j
public class FileUtils {

    /**
     * 文件分片
     *
     * @param inputFile
     * @param chunkSize
     * @return
     * @throws IOException
     */
    public static List<String> sliceFile(String inputFile, int chunkSize) throws IOException {
        List<String> outputFiles = new ArrayList<>();
        try (FileInputStream inputStream = new FileInputStream(inputFile);
             FileChannel inputChannel = inputStream.getChannel()) {
            ByteBuffer buffer = ByteBuffer.allocate(chunkSize);
            int counter = 0;
            while (inputChannel.read(buffer) > 0) {
                buffer.flip();
                String outputFile = getSelfIncreasingUniqueFilenameAndMaintainFileType(inputFile);
                try (FileOutputStream outputStream = new FileOutputStream(outputFile);
                     FileChannel outputChannel = outputStream.getChannel()) {
                    outputChannel.write(buffer);
                }
                buffer.clear();
                counter++;
                outputFiles.add(outputFile);
            }
        }
        return outputFiles;
    }

    /**
     * 文件合并
     * * FileOutputStream实现*
     *
     * @throws IOException
     */
    public static void mergeFile() throws IOException {
        /**
         * 需求：使用SequenceInputStream类来合并碎片文件
         * 1.创建一个list集合,来保存指定文件夹碎片流集合
         * 2.用集合工具类方法Collections.enumeration()方法将list集合转换为Enumeration
         * 3.新建一个SequenceInputStream流对象，并传入第2步的Enumeration
         * 4.创建一个输出流对象，创建缓冲区循环写第3步SequenceInputStream读取的内容
         */

        String fileName = "大文件 2.zip";
        File partDir = new File("/Users/chengluchao/Desktop/jingjingyunchang");
        List<FileInputStream> list = new ArrayList<>();
        for (int i = 0; i < 549; i++) {
            FileInputStream fis = new FileInputStream(new File(partDir, fileName + i));
            list.add(fis);
        }
        Enumeration<FileInputStream> en = Collections.enumeration(list);
        SequenceInputStream sis = new SequenceInputStream(en);

        FileOutputStream fos = new FileOutputStream(new File(partDir, fileName));

        byte[] buf = new byte[1024];
        int len;
        while ((len = sis.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }

        fos.flush();
        fos.close();
        sis.close();
    }

    /**
     * 文件合并
     * * FileChannel.write实现*
     *
     * @throws IOException
     */
    public static void mergeFileV2() throws IOException {
        final File targetDir = new File("/Users/chengluchao/Desktop/jingjingyunchang");
        final File sourceDir = new File("/Users/chengluchao/Desktop/jingjingyunchang");
        String fileName = "大文件 2.zip";
        int numberOfShards = 548;

        // 写出的目标文件
        FileOutputStream targetFileOutputStream = new FileOutputStream(new File(targetDir, fileName), true);
        FileChannel fileOutputStreamChannel = targetFileOutputStream.getChannel();


        for (int i = 0; i <= numberOfShards; i++) {
            // 读取的分片文件
            FileInputStream sourceFileInputStream = new FileInputStream(new File(sourceDir, fileName + i));
            FileChannel fileInputStreamChannel = sourceFileInputStream.getChannel();

            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            while (fileInputStreamChannel.read(byteBuffer) != -1) {
                byteBuffer.flip();
                fileOutputStreamChannel.write(byteBuffer);
                byteBuffer.clear();
            }
            fileInputStreamChannel.close();
            sourceFileInputStream.close();
        }

        fileOutputStreamChannel.close();
        targetFileOutputStream.flush();
        targetFileOutputStream.close();

    }

    /**
     * * 文件合并*
     * * FileChannel.transferTo 实现**
     *
     * @param fileName       文件名
     * @param sourceDirPath  源文件目录
     * @param targetDirPath  目标文件目录
     * @param numberOfShards 分片数
     * @throws IOException io
     */
    public static void mergeFileV3(String fileName, String sourceDirPath, String targetDirPath, int numberOfShards) throws IOException {
        mergeFileV3(fileName, sourceDirPath, targetDirPath, 0, numberOfShards);
    }

    /**
     * * 区间【前包后不包】
     * 适用于知道文件夹和编号固定的情况
     *
     * @param fileName
     * @param sourceDirPath
     * @param targetDirPath
     * @param start         开始
     * @param end           结束
     * @throws IOException
     */
    public static void mergeFileV3(String fileName, String sourceDirPath, String targetDirPath, int start, int end) throws IOException {
        List<String> sourceFiles = new ArrayList<>();
        for (int i = start; i < end; i++) {
            String fName = sourceDirPath + File.separator + fileName + i;
            sourceFiles.add(fName);
        }
        mergeFileV3(fileName, targetDirPath, sourceFiles);
    }

    /**
     * 文件合并*
     *
     * @param fileName      文件名
     * @param targetDirPath 目标路径
     * @param sourceFiles   源文件列表
     * @throws IOException io
     */
    public static void mergeFileV3(String fileName, String targetDirPath, List<String> sourceFiles) throws IOException {
        if (!FileUtil.exist(targetDirPath)) {
            FileUtil.mkdir(targetDirPath);
        }
        // 获取文件地址
        final File targetDir = new File(targetDirPath);

        // 写出的目标文件
        FileOutputStream targetFileOutputStream = new FileOutputStream(new File(targetDir, fileName), true);
        FileChannel targetFileOutputStreamChannel = targetFileOutputStream.getChannel();

        sourceFiles.forEach(sourceFile -> {
            try {
                // 读取的分片文件
                FileInputStream sourceFileInputStream = new FileInputStream(sourceFile);
                FileChannel sourceFileInputStreamChannel = sourceFileInputStream.getChannel();
                sourceFileInputStreamChannel.transferTo(0, sourceFileInputStreamChannel.size(), targetFileOutputStreamChannel);
                sourceFileInputStream.close();
                log.debug("{}完成分片复原", fileName);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        // io 断后
        targetFileOutputStreamChannel.close();
        targetFileOutputStream.flush();
        targetFileOutputStream.close();
    }

    public static void mkdir(String targetDirPath) {
        if (!FileUtil.exist(targetDirPath)) {
            FileUtil.mkdir(targetDirPath);
        }
    }

    public static void delete(String diskPath) {

        try {
            // 创建File对象
            File file = new File(diskPath);
            // 如果文件存在，则删除它
            if (file.exists()) {
                file.delete();
            }
        } catch (Exception e) {
            log.error("文件删除异常", e);
        }
    }


    /**
     * 统计文件夹下文件数量
     *
     * @param path
     * @return
     */
    public static int countTheNumberOfFilesInTheDirectory(String path) {
        try {
            File folder = new File(path);
            return folder.listFiles().length;
        } catch (Exception e) {
            throw new FlowerFallingException("文件夹操作失败");
        }
    }



    /**
     * 获取自增唯一文件名并维护文件类型
     *
     * @param filename
     * @return
     */

    public static String getSelfIncreasingUniqueFilenameAndMaintainFileType(String filename) {
        return getSelfIncreasingUniqueFilenameAndMaintainFileType(filename, null);
    }

    /**
     * 获取自增唯一文件名并维护文件类型
     *
     * @param filename
     * @param fileExtension
     * @return
     */
    public static String getSelfIncreasingUniqueFilenameAndMaintainFileType(String filename, String fileExtension) {
        fileExtension = fileExtension == null ? getFileExtension(filename) : fileExtension;
        String sourceName = removeFileExtension(filename);
        String name = sourceName;
        int counter = 0;
        while (Files.exists(Paths.get(name + "." + fileExtension))) {
            name = sourceName + "(" + counter++ + ")";
        }
        return name + "." + fileExtension;
    }

    /**
     * 获取文件扩展名
     *
     * @param fileName
     * @return
     */
    public static String getFileExtension(String fileName) {
        int dotIndex = fileName.lastIndexOf(".");
        if (dotIndex == -1) {
            return ""; // 如果没有后缀名，则返回空字符串
        } else {
            return fileName.substring(dotIndex + 1);
        }
    }


    /**
     * 去除后缀名
     *
     * @param filePath
     * @return
     */
    public static String removeFileExtension(String filePath) {
        File file = new File(filePath);
        int dotIndex = filePath.lastIndexOf(".");
        if (dotIndex == -1) {
            return filePath; // 如果没有后缀名，则返回原文件名
        } else {
            return filePath.substring(0, dotIndex);
        }
    }

    /**
     * 获取文件扩展名
     * @return
     */
    public static String getExtName(String filename) {
        if (StringUtils.isEmpty(filename)) {
            return "";
        }
        int index = filename.lastIndexOf(".");
        return index == -1 ? "" : filename.substring(index + 1);
    }



    public static void main(String[] args) throws IOException {
        String fileName = "IMG_2352_1.JPG";
        final String targetDir = "/Users/chengluchao/Desktop/jingjingyunchang/box/1";
        final String sourceDir = "/Users/chengluchao/Desktop/jingjingyunchang/vip/warehouse/202209212035234232";
        int numberOfShards = 12;
        mergeFileV3(fileName, sourceDir, targetDir, numberOfShards);
    }
}
